package playn.game.core;

import java.util.HashMap;

import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.World;

import playn.core.CanvasImage;
import playn.core.Font;
import playn.core.Image;
import playn.core.ImageLayer;
import playn.core.ImmediateLayer;
import playn.core.TextFormat;
import playn.core.TextLayout;
import playn.game.entities.Sonic;

import static playn.core.PlayN.*;

import tiled.simple.core.Map;
import tiled.simple.core.MapObject;
import tiled.simple.core.ObjectGroup;

public class Game implements playn.core.Game {
	
	public static int SCREEN_WIDTH=320;
	public static int SCREEN_HEIGHT=240;
	public static boolean initialized=false;
	
	public static final float PIXEL_PER_METER=500;

	public World world;
	private HashMap<String, Image> images;
	
	private HashMap<String,Map> maps;
	
	private StaticRenderer staticRenderer;
	public DynamicRenderer dynamicRenderer;

	private MapObjectsProvider mapObjectsProvider;
	private MapShapesProvider mapShapesProvider;

	public GameContactListener contactListener;

	public Controls controls;

	// for FPS
	private FPSCounter fps=new FPSCounter();
	private TextLayout fpsTextLayout=null;

	public Map animationGraphics;
	public ObjectGroup animations;


	public Game(Controls controls,HashMap<String,Map> maps,HashMap<String, Image> images) {
		this.maps=maps;
		this.images=images;
		
		this.animationGraphics=maps.get("graphics.tmx");
		this.animations=animationGraphics.getObjectGroup("animations");

		
		
		
		
		this.controls=controls;
	}

	public static Map currentMap;
	
	private ImageLayer statisticsLayer;

	ImmediateLayer dynamicLayer;
	ImmediateLayer vectorLayer;
	VectorRenderer vectorRenderer;
	
	public void init() {

		graphics().rootLayer().setScale(4,4);


		vectorRenderer=new VectorRenderer();
		vectorLayer=graphics().createImmediateLayer(SCREEN_WIDTH,SCREEN_HEIGHT,vectorRenderer);
		graphics().rootLayer().add(vectorLayer);

		staticRenderer=new StaticRenderer(images);
		staticRenderer.layer=graphics().createSurfaceLayer(SCREEN_WIDTH,SCREEN_HEIGHT);
		graphics().rootLayer().add(staticRenderer.layer);

		switchTo(maps.get("start.tmx"));
		
		
		keyboard().setListener(controls);
		
		// for FPS
		statisticsLayer=graphics().createImageLayer();
		graphics().rootLayer().add(statisticsLayer);

		initialized=true;
	}

	public void update(float delta) {
		world.step((float)delta/1000, 100, 100);
		mapObjectsProvider.update(delta);
		
		
		// switch screens
		Entity hero=mapObjectsProvider.hero;
		if(hero!=null){
			Vec2 position=hero.getBodyPosition();

			String right=currentMap.getProperty("Right");
			String left=currentMap.getProperty("Left");
		
			if(position.x<0){
				position.x+=SCREEN_WIDTH/PIXEL_PER_METER;
				hero.setBodyPosition(position);
				if(left!=null){
					switchTo(maps.get(left));
				}
			}

			if(position.x>SCREEN_WIDTH/PIXEL_PER_METER){
				position.x-=SCREEN_WIDTH/PIXEL_PER_METER;
				hero.setBodyPosition(position);
				if(right!=null){
					switchTo(maps.get(right));
				}
			}

			if(position.y<0){
				position.y+=SCREEN_HEIGHT/PIXEL_PER_METER;
				hero.setBodyPosition(position);
			}

			if(position.y>SCREEN_HEIGHT/PIXEL_PER_METER){
				position.y-=SCREEN_HEIGHT/PIXEL_PER_METER;
				hero.setBodyPosition(position);
			}

		}
	}


	private void switchTo(Map map){
		currentMap=map;

		// save hero position+velocity
		Vec2 heroPosition=null;
		Vec2 heroVelocity=null;
		if(mapObjectsProvider!=null){
			heroPosition=mapObjectsProvider.hero.body.getPosition();
			heroVelocity=mapObjectsProvider.hero.body.getLinearVelocity();
		}
		
		boolean doSleep = true;
		Vec2 gravity = new Vec2(0.0f, 10.0f);
		this.world = new World(gravity,doSleep);

		this.contactListener = new GameContactListener();
		this.world.setContactListener(contactListener);

		mapShapesProvider = new MapShapesProvider(this.world);
		mapObjectsProvider = new MapObjectsProvider(this);

		dynamicRenderer=new DynamicRenderer(mapObjectsProvider,images);

		if(dynamicLayer!=null){
			dynamicLayer.destroy();
		}

		dynamicLayer=graphics().createImmediateLayer(SCREEN_WIDTH,SCREEN_HEIGHT,dynamicRenderer);
		graphics().rootLayer().add(dynamicLayer);

		ObjectGroup collisions=currentMap.getObjectGroup("Collisions");
		if(collisions!=null){
			collisions.loadShapes(mapShapesProvider);
		} 
		
		ObjectGroup entities=currentMap.getObjectGroup("Entities");
		if(entities!=null){
			entities.loadObjects(mapObjectsProvider);	
		}

		
		//restore Hero position+velocity
		if(heroPosition!=null){
			MapObject sonicObject=new MapObject();
			sonicObject.setX((int)(heroPosition.x*PIXEL_PER_METER));
			sonicObject.setY((int)(heroPosition.y*PIXEL_PER_METER));
			sonicObject.setType("Hero");
			mapObjectsProvider.hero=new Sonic(this,sonicObject);
			mapObjectsProvider.entities.add(mapObjectsProvider.hero);
			mapObjectsProvider.hero.body.setLinearVelocity(heroVelocity);
		}

		
		staticRenderer.layer.surface().clear();
		
		vectorRenderer.init();
	}
	
	public void paint(float alpha) {
		
		paintMap(currentMap);
		
		fps.frame();
		
		// for FPS
		Font font = graphics().createFont("Times", Font.Style.BOLD, 15f);
        TextFormat format = new TextFormat().withFont(font);
		fpsTextLayout=graphics().layoutText("FPS="+fps.get(), format);
		CanvasImage c=graphics().createImage(100, 100);
		c.canvas().setFillColor(0xFFFFFFFF);
		c.canvas().fillText(fpsTextLayout, 1f, 1f);
		statisticsLayer.setImage(c);
	}

	
	public void paintMap(Map map){
		
		map.render("Water",staticRenderer);
		map.render("Waterfall",staticRenderer);
		map.render("Walls",staticRenderer);
		map.render("Doors",staticRenderer);
		map.render("Bottom",staticRenderer);
		
	}
	
	public int updateRate() {
		return 15;
	}

}
